This file is currently a template for running evaluation experiments. You should update it according to your codes but following precisely the same structure.
parameter tuning
1.First, you build the xgboost model using default parameters. You might be surprised to see that default parameters sometimes give impressive accuracy.
2.If you get a depressing model accuracy, do this: fix eta = 0.1, leave the rest of the parameters at default value, using xgb.cv function get best n_rounds. Now, build a model with these parameters and check the accuracy.
3.Otherwise, you can perform a grid search on rest of the parameters (max_depth, gamma, subsample, colsample_bytree etc) by fixing eta and nrounds. Note: If using gbtree, don’t introduce gamma until you see a significant difference in your train and test error.
4.Using the best parameters from grid search, tune the regularization parameters(alpha,lambda) if required.
5.At last, increase/decrease eta and follow the procedure. But remember, excessively lower eta values would allow the model to learn deep interactions in the data and in this process, it might capture noise. So be careful!
#preparing matrix
feature_train = as.matrix(dat_train[, -6007])
label_train = as.integer(dat_train$label)-1
#imbalanced data, needs smaller scale_pos_weight
sumwpos<-sum(label_train==1)
sumwneg<-sum(label_train==0)
dtrain<-xgb.DMatrix(feature_train,label=label_train)
run.cv_XGS.Step1<-FALSE
if(run.cv_XGS.Step1){
params <- list(booster = "gbtree", objective = "binary:logistic", eta=0.3, gamma=0, max_depth=5, min_child_weight=1, subsample=0.8, colsample_bytree=0.8,scale_pos_weight=sumwneg/sumwpos)
xgbcv <- xgb.cv( params = params, data = dtrain, nrounds = 200, nfold = 5, showsd = T, stratified = T, print_every_n = 10, early_stop_round = 20, maximize = F,eval_metric="auc",eval_metric="error")
result<-xgbcv$evaluation_logt
save(result, file="../output/res_cv_XGS.Step1.RData")
}else{
load("../output/res_cv_XGS.Step1.RData")}
result_table<-rbind(result[which.min(result$test_error_mean),c("iter","test_error_mean","test_error_std","test_auc_mean","test_auc_std")],
result[which.max(result$test_auc_mean),c("iter","test_error_mean","test_error_std","test_auc_mean","test_auc_std")])
#best iter 160/199
#preprocessing the matrix for the test
feature_test<-as.matrix(dat_test[,-6007])
label_test<- as.integer(dat_test$label)-1
dtest<-xgb.DMatrix(feature_test,label=label_test)
#first model trainig using
run.default.model=FALSE
if(run.default.model){
xgb1<-xgb.train(params=params,
data=dtrain,
nrounds=160,
watchlist=list(val=dtest,train=dtrain),
print_every_n=10,
early_stop_round=10,
eval_metric="error",
eval_metric="auc")
#model prediction
xgbpred.prob<- predict(xgb1,dtest)
xgbpred.label<- ifelse(xgbpred.prob>0.5,1,0)
levels(factor(xgbpred))
levels(factor(label_test))
library(caret)
confM<-confusionMatrix(factor(xgbpred.label),factor(label_test))
#accuracy 0.8433
#balanced accuracy 0.6886
auc<-roc(factor(label_test),xgbpred.prob)$auc
#auc:0.8489(high)
# view variable importance plot
mat<- xgb.importance(feature_names=colnames(feature_train),model=xgb1)
xgb.plot.importance(importance_matrix = mat[1:20])
save(confM,auc,mat,file="../output/res_cv_XGS.toy.RData")
}else{
load("../output/res_cv_XGS.toy.RData")
}
#try to find the max depth and min child weight
#source("../lib/cross_validation_XGS.R")
hyper_grid_XGB_1<- expand.grid(max_depth=3,#seq(3,10,by=2),
min_child_weight=1,#seq(1,6,by=2),
subsample=0.8,
colsample_bytree=0.8
)
parallelStartSocket(cpus = detectCores())
dim(hyper_grid_XGB_1)
feature_train = as.matrix(dat_train[, -6007])
label_train = as.integer(dat_train$label)
run.cv.XGS1=TRUE
if(run.cv.XGS1){
res_cv_XGS1 <- matrix(0, nrow = nrow(hyper_grid_XGB_1), ncol = 4)
for(i in 1:nrow(hyper_grid_XGB_1)){
l<-list(max_depth=hyper_grid_XGB_1$max_depth[i],
min_child_weight=hyper_grid_XGB_1$min_child_weight[i],
subsample=hyper_grid_XGB_1$subsample[i],
colsample_bytree=hyper_grid_XGB_1$colsample_bytree[i])
xgbcv<-xgb.cv( params = l,
data = dtrain,
nrounds = 160,
nfold = K,
gamma=0,
showsd = T,
stratified = T,
early_stop_round = 10,
print_every_n=80,
eval_metric="auc",
eval_metric="error",
booster = "gbtree",
objective = "binary:logistic",
scale_pos_weight=sumwneg/sumwpos,
eta=0.3
)
result<-xgbcv$evaluation_log
cv.error<-result$test_error_mean
cv.AUC<-result$test_auc_mean
res_cv_XGS1[i]<-c(mean(cv.error),sd(cv.error), mean(cv.AUC), sd(cv.AUC))
save(res_cv_XGS1, file="../output/res_cv_XGS1.RData")
}
}else{
load("../output/res_cv_XGS1.RData")
}
#no need to tune gamma
hyper_grid_XGB_2<- expand.grid(max_depth=par_best_depth),
min_child_weight=par_best_child),
subsample=c(0.6,0.7,0.8,0.9),
colsample_bytree=c(0.6,0.7,0.8,0.9)
)
parallelStartSocket(cpus = detectCores())
dim(hyper_grid_XGB_2)
feature_train = as.matrix(dat_train[, -6007])
label_train = as.integer(dat_train$label)
run.cv.XGS2=TRUE
if(run.cv.XGS2){
res_cv_XGS2 <- matrix(0, nrow = nrow(hyper_grid_XGB_1), ncol = 4)
for(i in 1:nrow(hyper_grid_XGB_2)){
l<-list(max_depth=hyper_grid_XGB_2$max_depth[i],
min_child_weight=hyper_grid_XGB_2$min_child_weight[i],
subsample=hyper_grid_XGB_2$subsample[i],
colsample_bytree=hyper_grid_XGB_2$colsample_bytree[i])
xgbcv<-xgb.cv( params = l,
data = dtrain,
nrounds = 160,
nfold = K,
gamma=0,
showsd = T,
stratified = T,
early_stop_round = 20,
eval_metric="auc",
eval_metric="error",
booster = "gbtree",
objective = "binary:logistic",
scale_pos_weight=sumwneg/sumwpos,
eta=0.3
)
result<-xgbcv$evaluation_log
cv.error<-result$test_error_mean
cv.AUC<-result$test_auc_mean
res_cv_XGS2<-c(mean(cv.error),sd(cv.error), mean(cv.AUC), sd(cv.AUC))
save(res_cv_XGS2, file="../output/res_cv_XGS2.RData")
}
}else{
load("../output/res_cv_XGS2.RData")
}
#tune the regularization parameters
xgb.cv( params = params, data = dtrain, nrounds = 200, nfold = 5, showsd = T, stratified = T, print_every_n = 10, early_stop_round = 20, maximize = F,eval_metric="auc",eval_metric="error")
traintask<-makeClassifTask(data=dat_train,target="label")
testtask<- makeClassifTask(data=dat_test,target="label")
cv.para.search=TRUE
if(cv.para.search){
#create learner
lrn_1 <- makeLearner("classif.xgboost",predict.type = "response")
lrn_1$par.vals <- list( objective="binary:logistic", eval_metric="error",eval_metric="auc", nrounds=200L, eta=0.3,scale_pos_weight=sumwneg/sumwpos)
#set parameter space
params_1<- makeParamSet(
makeIntegerParam("max_depth", lower=3L, upper=10L),
makeNumericParam("min_child_weight", lower=1L, upper=10L),
makeNumericParam("subsample",lower = 0.5,upper = 1),
makeNumericParam("colsample_bytree",lower = 0.5,upper = 1)
)
#set resampling strategy
rdesc_1<- makeResampleDesc("CV", stratify=T, iters=5L)
#search strategy
ctrl_1<- makeTuneControlRandom(maxit=100L)#100 models
#set parallel backend
parallelStartSocket(cpus = detectCores())
mytune_1 <- tuneParams(learner = lrn_1, task = traintask, resampling = rdesc_1, measures = acc, par.set = params_1, control = ctrl_1, show.info = T)
save(mytune_1, file="../output/res_cv_XGS.Step2.RData")
}else{
load("../output/res_cv_XGS.Step2.RData")
}
mytune_1$y
#set hyperparameters
lrn_tune_1 <- setHyperPars(lrn_1,par.vals = mytune_1$x)
#train model
xgmodel_1 <- train(learner = lrn_tune_1,task = traintask)
#predict model
xgpred_1.prob<- predict(xgmodel_1,testtask)
confusionMatrix(xgpred_1$data$response,xgpred_1$data$truth)# accracy=0.835, balanced accuracy=0.6544
auc_1<-roc(factor(label_test),xgbpred_1.prob)$auc #0.84 same as the default
#Result:Op. pars: max_depth=10; min_child_weight=9.93; subsample=0.846; colsample_bytree=0.627
#acc.test.mean=0.8441751, smaller than the default, why??????
#need auc and accuracy from confusion matrix
# regulariztion tuning
#high dimention using alpha to have a try
#may be
param_alpha<-
LS0tCnRpdGxlOiAiTWFpbiIKYXV0aG9yOiAiV2Vpd2VpIFNvbmciCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKSW4geW91ciBmaW5hbCByZXBvLCB0aGVyZSBzaG91bGQgYmUgYW4gUiBtYXJrZG93biBmaWxlIHRoYXQgb3JnYW5pemVzICoqYWxsIGNvbXB1dGF0aW9uYWwgc3RlcHMqKiBmb3IgZXZhbHVhdGluZyB5b3VyIHByb3Bvc2VkIEZhY2lhbCBFeHByZXNzaW9uIFJlY29nbml0aW9uIGZyYW1ld29yay4gCgpUaGlzIGZpbGUgaXMgY3VycmVudGx5IGEgdGVtcGxhdGUgZm9yIHJ1bm5pbmcgZXZhbHVhdGlvbiBleHBlcmltZW50cy4gWW91IHNob3VsZCB1cGRhdGUgaXQgYWNjb3JkaW5nIHRvIHlvdXIgY29kZXMgYnV0IGZvbGxvd2luZyBwcmVjaXNlbHkgdGhlIHNhbWUgc3RydWN0dXJlLiAKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmlmKCFyZXF1aXJlKCJFQkltYWdlIikpewogIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiRUJJbWFnZSIpCn0KaWYoIXJlcXVpcmUoIlIubWF0bGFiIikpewogIGluc3RhbGwucGFja2FnZXMoIlIubWF0bGFiIikKfQppZighcmVxdWlyZSgicmVhZHhsIikpewogIGluc3RhbGwucGFja2FnZXMoInJlYWR4bCIpCn0KCmlmKCFyZXF1aXJlKCJkcGx5ciIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCn0KaWYoIXJlcXVpcmUoInJlYWR4bCIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkeGwiKQp9CgppZighcmVxdWlyZSgiZ2dwbG90MiIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKfQoKaWYoIXJlcXVpcmUoImNhcmV0IikpewogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKfQoKaWYoIXJlcXVpcmUoImdsbW5ldCIpKXsKICBpbnN0YWxsLnBhY2thZ2VzKCJnbG1uZXQiKQp9CgppZighcmVxdWlyZSgiV2VpZ2h0ZWRST0MiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiV2VpZ2h0ZWRST0MiKQp9CmlmKCFyZXF1aXJlKCJETXdSIikpewogIGluc3RhbGwucGFja2FnZXMoIkRNd1IiKQp9CmlmKCFyZXF1aXJlKCJtbHIiKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygibWxyIikKfQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHBhcmFsbGVsTWFwKQpsaWJyYXJ5KG1scikKbGlicmFyeShSLm1hdGxhYikKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoRUJJbWFnZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKbGlicmFyeShXZWlnaHRlZFJPQykKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoeGdib29zdCkKbGlicmFyeShETXdSKQpsaWJyYXJ5KHBST0MpCgpgYGAKCiMjIyBTdGVwIDAgc2V0IHdvcmsgZGlyZWN0b3JpZXMKYGBge3Igd2tkaXIsIGV2YWw9RkFMU0V9CnNldC5zZWVkKDIwMjApCiMgc2V0d2QoIn4vUHJvamVjdDMtRmFjaWFsRW1vdGlvblJlY29nbml0aW9uL2RvYyIpCiMgaGVyZSByZXBsYWNlIGl0IHdpdGggeW91ciBvd24gcGF0aCBvciBtYW51YWxseSBzZXQgaXQgaW4gUlN0dWRpbyB0byB3aGVyZSB0aGlzIHJtZCBmaWxlIGlzIGxvY2F0ZWQuIAojIHVzZSByZWxhdGl2ZSBwYXRoIGZvciByZXByb2R1Y2liaWxpdHkKYGBgCgpQcm92aWRlIGRpcmVjdG9yaWVzIGZvciB0cmFpbmluZyBpbWFnZXMuIFRyYWluaW5nIGltYWdlcyBhbmQgVHJhaW5pbmcgZmlkdWNpYWwgcG9pbnRzIHdpbGwgYmUgaW4gZGlmZmVyZW50IHN1YmZvbGRlcnMuIApgYGB7cn0KdHJhaW5fZGlyIDwtICIuLi9kYXRhL3RyYWluX3NldC8iICMgVGhpcyB3aWxsIGJlIG1vZGlmaWVkIGZvciBkaWZmZXJlbnQgZGF0YSBzZXRzLgp0cmFpbl9pbWFnZV9kaXIgPC0gcGFzdGUodHJhaW5fZGlyLCAiaW1hZ2VzLyIsIHNlcD0iIikKdHJhaW5fcHRfZGlyIDwtIHBhc3RlKHRyYWluX2RpciwgICJwb2ludHMvIiwgc2VwPSIiKQp0cmFpbl9sYWJlbF9wYXRoIDwtIHBhc3RlKHRyYWluX2RpciwgImxhYmVsLmNzdiIsIHNlcD0iIikgCmBgYAoKIyMjIFN0ZXAgMTogc2V0IHVwIGNvbnRyb2xzIGZvciBldmFsdWF0aW9uIGV4cGVyaW1lbnRzLgoKSW4gdGhpcyBjaHVuaywgd2UgaGF2ZSBhIHNldCBvZiBjb250cm9scyBmb3IgdGhlIGV2YWx1YXRpb24gZXhwZXJpbWVudHMuIAoKKyAoVC9GKSBjcm9zcy12YWxpZGF0aW9uIG9uIHRoZSB0cmFpbmluZyBzZXQKKyAoVC9GKSByZXdlaWdodGluZyB0aGUgc2FtcGxlcyBmb3IgdHJhaW5pbmcgc2V0IAorIChudW1iZXIpIEssIHRoZSBudW1iZXIgb2YgQ1YgZm9sZHMKKyAoVC9GKSBwcm9jZXNzIGZlYXR1cmVzIGZvciB0cmFpbmluZyBzZXQKKyAoVC9GKSBydW4gZXZhbHVhdGlvbiBvbiBhbiBpbmRlcGVuZGVudCB0ZXN0IHNldAorIChUL0YpIHByb2Nlc3MgZmVhdHVyZXMgZm9yIHRlc3Qgc2V0CgpgYGB7ciBleHBfc2V0dXB9CnJ1bi5jdiA8LSBGQUxTRSAjIHJ1biBjcm9zcy12YWxpZGF0aW9uIG9uIHRoZSB0cmFpbmluZyBzZXQKc2FtcGxlLnJld2VpZ2h0IDwtIFRSVUUgIyBydW4gc2FtcGxlIHJld2VpZ2h0aW5nIGluIG1vZGVsIHRyYWluaW5nCksgPC0gNSAgIyBudW1iZXIgb2YgQ1YgZm9sZHMKcnVuLmZlYXR1cmUudHJhaW4gPC0gVFJVRSAjIHByb2Nlc3MgZmVhdHVyZXMgZm9yIHRyYWluaW5nIHNldApydW4udGVzdCA8LSBUUlVFICMgcnVuIGV2YWx1YXRpb24gb24gYW4gaW5kZXBlbmRlbnQgdGVzdCBzZXQKcnVuLmZlYXR1cmUudGVzdCA8LSBUUlVFICMgcHJvY2VzcyBmZWF0dXJlcyBmb3IgdGVzdCBzZXQKYGBgCgpVc2luZyBjcm9zcy12YWxpZGF0aW9uIG9yIGluZGVwZW5kZW50IHRlc3Qgc2V0IGV2YWx1YXRpb24sIHdlIGNvbXBhcmUgdGhlIHBlcmZvcm1hbmNlIG9mIG1vZGVscyB3aXRoIGRpZmZlcmVudCBzcGVjaWZpY2F0aW9ucy4gSW4gdGhpcyBTdGFydGVyIENvZGUsIHdlIHR1bmUgcGFyYW1ldGVyIGxhbWJkYSAodGhlIGFtb3VudCBvZiBzaHJpbmthZ2UpIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggTEFTU08gcGVuYWx0eS4KCiMjIyBTdGVwIDI6IGltcG9ydCBkYXRhIGFuZCB0cmFpbi10ZXN0IHNwbGl0IApgYGB7cn0KI3RyYWluLXRlc3Qgc3BsaXQKaW5mbyA8LSByZWFkLmNzdih0cmFpbl9sYWJlbF9wYXRoKQpuIDwtIG5yb3coaW5mbykKbl90cmFpbiA8LSByb3VuZChuKig0LzUpLCAwKQp0cmFpbl9pZHggPC0gc2FtcGxlKGluZm8kSW5kZXgsIG5fdHJhaW4sIHJlcGxhY2UgPSBGKQp0ZXN0X2lkeCA8LSBzZXRkaWZmKGluZm8kSW5kZXgsIHRyYWluX2lkeCkKYGBgCgpJZiB5b3UgY2hvb3NlIHRvIGV4dHJhY3QgZmVhdHVyZXMgZnJvbSBpbWFnZXMsIHN1Y2ggYXMgdXNpbmcgR2Fib3IgZmlsdGVyLCBSIG1lbW9yeSB3aWxsIGV4aGF1c3QgYWxsIGltYWdlcyBhcmUgcmVhZCB0b2dldGhlci4gVGhlIHNvbHV0aW9uIGlzIHRvIHJlcGVhdCByZWFkaW5nIGEgc21hbGxlciBiYXRjaChlLmcgMTAwKSBhbmQgcHJvY2VzcyB0aGVtLiAKYGBge3J9Cm5fZmlsZXMgPC0gbGVuZ3RoKGxpc3QuZmlsZXModHJhaW5faW1hZ2VfZGlyKSkKCmltYWdlX2xpc3QgPC0gbGlzdCgpCmZvcihpIGluIDE6MTAwKXsKICAgaW1hZ2VfbGlzdFtbaV1dIDwtIHJlYWRJbWFnZShwYXN0ZTAodHJhaW5faW1hZ2VfZGlyLCBzcHJpbnRmKCIlMDRkIiwgaSksICIuanBnIikpCn0KYGBgCgpGaWR1Y2lhbCBwb2ludHMgYXJlIHN0b3JlZCBpbiBtYXRsYWIgZm9ybWF0LiBJbiB0aGlzIHN0ZXAsIHdlIHJlYWQgdGhlbSBhbmQgc3RvcmUgdGhlbSBpbiBhIGxpc3QuCmBgYHtyIHJlYWQgZmlkdWNpYWwgcG9pbnRzfQojZnVuY3Rpb24gdG8gcmVhZCBmaWR1Y2lhbCBwb2ludHMKI2lucHV0OiBpbmRleAojb3V0cHV0OiBtYXRyaXggb2YgZmlkdWNpYWwgcG9pbnRzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGluZGV4CnJlYWRNYXQubWF0cml4IDwtIGZ1bmN0aW9uKGluZGV4KXsKICAgICByZXR1cm4ocm91bmQocmVhZE1hdChwYXN0ZTAodHJhaW5fcHRfZGlyLCBzcHJpbnRmKCIlMDRkIiwgaW5kZXgpLCAiLm1hdCIpKVtbMV1dLDApKQp9CgojbG9hZCBmaWR1Y2lhbCBwb2ludHMKZmlkdWNpYWxfcHRfbGlzdCA8LSBsYXBwbHkoMTpuX2ZpbGVzLCByZWFkTWF0Lm1hdHJpeCkKc2F2ZShmaWR1Y2lhbF9wdF9saXN0LCBmaWxlPSIuLi9vdXRwdXQvZmlkdWNpYWxfcHRfbGlzdC5SRGF0YSIpCmBgYAoKIyMjIFN0ZXAgMzogY29uc3RydWN0IGZlYXR1cmVzIGFuZCByZXNwb25zZXMKCisgVGhlIGZvbGxvdyBwbG90cyBzaG93IGhvdyBwYWlyd2lzZSBkaXN0YW5jZSBiZXR3ZWVuIGZpZHVjaWFsIHBvaW50cyBjYW4gd29yayBhcyBmZWF0dXJlIGZvciBmYWNpYWwgZW1vdGlvbiByZWNvZ25pdGlvbi4KCiAgKyBJbiB0aGUgZmlyc3QgY29sdW1uLCA3OCBmaWR1Y2lhbHMgcG9pbnRzIG9mIGVhY2ggZW1vdGlvbiBhcmUgbWFya2VkIGluIG9yZGVyLiAKICArIEluIHRoZSBzZWNvbmQgY29sdW1uIGRpc3RyaWJ1dGlvbnMgb2YgdmVydGljYWwgZGlzdGFuY2UgYmV0d2VlbiByaWdodCBwdXBpbCgxKSBhbmQgIHJpZ2h0IGJyb3cgcGVhaygyMSkgYXJlIHNob3duIGluICBoaXN0b2dyYW1zLiBGb3IgZXhhbXBsZSwgdGhlIGRpc3RhbmNlIG9mIGFuIGFuZ3J5IGZhY2UgdGVuZHMgdG8gYmUgc2hvcnRlciB0aGFuIHRoYXQgb2YgYSBzdXJwcmlzZWQgZmFjZS4KICArIFRoZSB0aGlyZCBjb2x1bW4gaXMgdGhlIGRpc3RyaWJ1dGlvbnMgb2YgdmVydGljYWwgZGlzdGFuY2VzIGJldHdlZW4gcmlnaHQgbW91dGggY29ybmVyKDUwKQphbmQgdGhlIG1pZHBvaW50IG9mIHRoZSB1cHBlciBsaXAoNTIpLiAgRm9yIGV4YW1wbGUsIHRoZSBkaXN0YW5jZSBvZiBhbiBoYXBweSBmYWNlIHRlbmRzIHRvIGJlIHNob3J0ZXIgdGhhbiB0aGF0IG9mIGEgc2FkIGZhY2UuCgohW0ZpZ3VyZTFdKC4uL2ZpZ3MvZmVhdHVyZV92aXN1YWxpemF0aW9uLmpwZykKCmBmZWF0dXJlLlJgIHNob3VsZCBiZSB0aGUgd3JhcHBlciBmb3IgYWxsIHlvdXIgZmVhdHVyZSBlbmdpbmVlcmluZyBmdW5jdGlvbnMgYW5kIG9wdGlvbnMuIFRoZSBmdW5jdGlvbiBgZmVhdHVyZSggKWAgc2hvdWxkIGhhdmUgb3B0aW9ucyB0aGF0IGNvcnJlc3BvbmQgdG8gZGlmZmVyZW50IHNjZW5hcmlvcyBmb3IgeW91ciBwcm9qZWN0IGFuZCBwcm9kdWNlcyBhbiBSIG9iamVjdCB0aGF0IGNvbnRhaW5zIGZlYXR1cmVzIGFuZCByZXNwb25zZXMgdGhhdCBhcmUgcmVxdWlyZWQgYnkgYWxsIHRoZSBtb2RlbHMgeW91IGFyZSBnb2luZyB0byBldmFsdWF0ZSBsYXRlci4gCiAgCiAgKyBgZmVhdHVyZS5SYAogICsgSW5wdXQ6IGxpc3Qgb2YgaW1hZ2VzIG9yIGZpZHVjaWFsIHBvaW50CiAgKyBPdXRwdXQ6IGFuIFJEYXRhIGZpbGUgdGhhdCBjb250YWlucyBleHRyYWN0ZWQgZmVhdHVyZXMgYW5kIGNvcnJlc3BvbmRpbmcgcmVzcG9uc2VzCgpgYGB7ciBmZWF0dXJlfQpzb3VyY2UoIi4uL2xpYi9mZWF0dXJlLlIiKQp0bV9mZWF0dXJlX3RyYWluIDwtIE5BCmlmKHJ1bi5mZWF0dXJlLnRyYWluKXsKICB0bV9mZWF0dXJlX3RyYWluIDwtIHN5c3RlbS50aW1lKGRhdF90cmFpbiA8LSBmZWF0dXJlKGZpZHVjaWFsX3B0X2xpc3QsIHRyYWluX2lkeCkpCiAgc2F2ZShkYXRfdHJhaW4sIGZpbGU9Ii4uL291dHB1dC9mZWF0dXJlX3RyYWluLlJEYXRhIikKfWVsc2V7CiAgbG9hZChmaWxlPSIuLi9vdXRwdXQvZmVhdHVyZV90cmFpbi5SRGF0YSIpCn0KCnRtX2ZlYXR1cmVfdGVzdCA8LSBOQQppZihydW4uZmVhdHVyZS50ZXN0KXsKICB0bV9mZWF0dXJlX3Rlc3QgPC0gc3lzdGVtLnRpbWUoZGF0X3Rlc3QgPC0gZmVhdHVyZShmaWR1Y2lhbF9wdF9saXN0LCB0ZXN0X2lkeCkpCiAgc2F2ZShkYXRfdGVzdCwgZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdGVzdC5SRGF0YSIpCn1lbHNlewogIGxvYWQoZmlsZT0iLi4vb3V0cHV0L2ZlYXR1cmVfdGVzdC5SRGF0YSIpCn0KCgpgYGAKCiMjIyBTdGVwIDQ6IFRyYWluIGEgY2xhc3NpZmljYXRpb24gbW9kZWwgd2l0aCB0cmFpbmluZyBmZWF0dXJlcyBhbmQgcmVzcG9uc2VzCkNhbGwgdGhlIHRyYWluIG1vZGVsIGFuZCB0ZXN0IG1vZGVsIGZyb20gbGlicmFyeS4gCgpgdHJhaW4uUmAgYW5kIGB0ZXN0LlJgIHNob3VsZCBiZSB3cmFwcGVycyBmb3IgYWxsIHlvdXIgbW9kZWwgdHJhaW5pbmcgc3RlcHMgYW5kIHlvdXIgY2xhc3NpZmljYXRpb24vcHJlZGljdGlvbiBzdGVwcy4gCgorIGB0cmFpbi5SYAogICsgSW5wdXQ6IGEgZGF0YSBmcmFtZSBjb250YWluaW5nIGZlYXR1cmVzIGFuZCBsYWJlbHMgYW5kIGEgcGFyYW1ldGVyIGxpc3QuCiAgKyBPdXRwdXQ6YSB0cmFpbmVkIG1vZGVsCisgYHRlc3QuUmAKICArIElucHV0OiB0aGUgZml0dGVkIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHVzaW5nIHRyYWluaW5nIGRhdGEgYW5kIHByb2Nlc3NlZCBmZWF0dXJlcyBmcm9tIHRlc3RpbmcgaW1hZ2VzIAogICsgSW5wdXQ6IGFuIFIgb2JqZWN0IHRoYXQgY29udGFpbnMgYSB0cmFpbmVkIGNsYXNzaWZpZXIuCiAgKyBPdXRwdXQ6IHRyYWluaW5nIG1vZGVsIHNwZWNpZmljYXRpb24KCiMjIyBwYXJhbWV0ZXIgdHVuaW5nIAoKICAxLkZpcnN0LCB5b3UgYnVpbGQgdGhlIHhnYm9vc3QgbW9kZWwgdXNpbmcgZGVmYXVsdCBwYXJhbWV0ZXJzLiBZb3UgbWlnaHQgYmUgc3VycHJpc2VkIHRvIHNlZSB0aGF0IGRlZmF1bHQgcGFyYW1ldGVycyBzb21ldGltZXMgZ2l2ZSBpbXByZXNzaXZlIGFjY3VyYWN5LgogIAogIDIuSWYgeW91IGdldCBhIGRlcHJlc3NpbmcgbW9kZWwgYWNjdXJhY3ksIGRvIHRoaXM6IGZpeCBldGEgPSAwLjEsIGxlYXZlIHRoZSByZXN0IG9mIHRoZSBwYXJhbWV0ZXJzIGF0IGRlZmF1bHQgdmFsdWUsIHVzaW5nIHhnYi5jdiBmdW5jdGlvbiBnZXQgYmVzdCBuX3JvdW5kcy4gTm93LCBidWlsZCBhIG1vZGVsIHdpdGggdGhlc2UgcGFyYW1ldGVycyBhbmQgY2hlY2sgdGhlIGFjY3VyYWN5LgogIAogIDMuT3RoZXJ3aXNlLCB5b3UgY2FuIHBlcmZvcm0gYSBncmlkIHNlYXJjaCBvbiByZXN0IG9mIHRoZSBwYXJhbWV0ZXJzIChtYXhfZGVwdGgsIGdhbW1hLCBzdWJzYW1wbGUsIGNvbHNhbXBsZV9ieXRyZWUgZXRjKSBieSBmaXhpbmcgZXRhIGFuZCBucm91bmRzLiBOb3RlOiBJZiB1c2luZyBnYnRyZWUsIGRvbid0IGludHJvZHVjZSBnYW1tYSB1bnRpbCB5b3Ugc2VlIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiB5b3VyIHRyYWluIGFuZCB0ZXN0IGVycm9yLgogIAogICA0LlVzaW5nIHRoZSBiZXN0IHBhcmFtZXRlcnMgZnJvbSBncmlkIHNlYXJjaCwgdHVuZSB0aGUgcmVndWxhcml6YXRpb24gcGFyYW1ldGVycyhhbHBoYSxsYW1iZGEpIGlmIHJlcXVpcmVkLgogICAKICA1LkF0IGxhc3QsIGluY3JlYXNlL2RlY3JlYXNlIGV0YSBhbmQgZm9sbG93IHRoZSBwcm9jZWR1cmUuIEJ1dCByZW1lbWJlciwgZXhjZXNzaXZlbHkgbG93ZXIgZXRhIHZhbHVlcyB3b3VsZCBhbGxvdyB0aGUgbW9kZWwgdG8gbGVhcm4gZGVlcCBpbnRlcmFjdGlvbnMgaW4gdGhlIGRhdGEgYW5kIGluIHRoaXMgcHJvY2VzcywgaXQgbWlnaHQgY2FwdHVyZSBub2lzZS4gU28gYmUgY2FyZWZ1bCEKICAKYGBge3IgWEdCdHVuZV9zdGVwMX0KI3ByZXBhcmluZyBtYXRyaXggCmZlYXR1cmVfdHJhaW4gPSBhcy5tYXRyaXgoZGF0X3RyYWluWywgLTYwMDddKQpsYWJlbF90cmFpbiA9IGFzLmludGVnZXIoZGF0X3RyYWluJGxhYmVsKS0xIAojaW1iYWxhbmNlZCBkYXRhLCBuZWVkcyBzbWFsbGVyIHNjYWxlX3Bvc193ZWlnaHQKc3Vtd3Bvczwtc3VtKGxhYmVsX3RyYWluPT0xKQpzdW13bmVnPC1zdW0obGFiZWxfdHJhaW49PTApCmR0cmFpbjwteGdiLkRNYXRyaXgoZmVhdHVyZV90cmFpbixsYWJlbD1sYWJlbF90cmFpbikKcnVuLmN2X1hHUy5TdGVwMTwtRkFMU0UKaWYocnVuLmN2X1hHUy5TdGVwMSl7CnBhcmFtcyA8LSBsaXN0KGJvb3N0ZXIgPSAiZ2J0cmVlIiwgb2JqZWN0aXZlID0gImJpbmFyeTpsb2dpc3RpYyIsIGV0YT0wLjMsIGdhbW1hPTAsIG1heF9kZXB0aD01LCBtaW5fY2hpbGRfd2VpZ2h0PTEsIHN1YnNhbXBsZT0wLjgsIGNvbHNhbXBsZV9ieXRyZWU9MC44LHNjYWxlX3Bvc193ZWlnaHQ9c3Vtd25lZy9zdW13cG9zKQp4Z2JjdiA8LSB4Z2IuY3YoIHBhcmFtcyA9IHBhcmFtcywgZGF0YSA9IGR0cmFpbiwgbnJvdW5kcyA9IDIwMCwgbmZvbGQgPSA1LCBzaG93c2QgPSBULCBzdHJhdGlmaWVkID0gVCwgcHJpbnRfZXZlcnlfbiA9IDEwLCBlYXJseV9zdG9wX3JvdW5kID0gMjAsIG1heGltaXplID0gRixldmFsX21ldHJpYz0iYXVjIixldmFsX21ldHJpYz0iZXJyb3IiKQogcmVzdWx0PC14Z2JjdiRldmFsdWF0aW9uX2xvZ3QKIHNhdmUocmVzdWx0LCBmaWxlPSIuLi9vdXRwdXQvcmVzX2N2X1hHUy5TdGVwMS5SRGF0YSIpCn1lbHNlewogIGxvYWQoIi4uL291dHB1dC9yZXNfY3ZfWEdTLlN0ZXAxLlJEYXRhIil9CgpyZXN1bHRfdGFibGU8LXJiaW5kKHJlc3VsdFt3aGljaC5taW4ocmVzdWx0JHRlc3RfZXJyb3JfbWVhbiksYygiaXRlciIsInRlc3RfZXJyb3JfbWVhbiIsInRlc3RfZXJyb3Jfc3RkIiwidGVzdF9hdWNfbWVhbiIsInRlc3RfYXVjX3N0ZCIpXSwKcmVzdWx0W3doaWNoLm1heChyZXN1bHQkdGVzdF9hdWNfbWVhbiksYygiaXRlciIsInRlc3RfZXJyb3JfbWVhbiIsInRlc3RfZXJyb3Jfc3RkIiwidGVzdF9hdWNfbWVhbiIsInRlc3RfYXVjX3N0ZCIpXSkKCiNiZXN0IGl0ZXIgMTYwLzE5OQoKI3ByZXByb2Nlc3NpbmcgdGhlIG1hdHJpeCBmb3IgdGhlIHRlc3QgCmZlYXR1cmVfdGVzdDwtYXMubWF0cml4KGRhdF90ZXN0WywtNjAwN10pCmxhYmVsX3Rlc3Q8LSBhcy5pbnRlZ2VyKGRhdF90ZXN0JGxhYmVsKS0xCmR0ZXN0PC14Z2IuRE1hdHJpeChmZWF0dXJlX3Rlc3QsbGFiZWw9bGFiZWxfdGVzdCkKCgojZmlyc3QgbW9kZWwgdHJhaW5pZyB1c2luZwpydW4uZGVmYXVsdC5tb2RlbD1GQUxTRSAKaWYocnVuLmRlZmF1bHQubW9kZWwpewp4Z2IxPC14Z2IudHJhaW4ocGFyYW1zPXBhcmFtcywKICAgICAgICAgICAgICAgIGRhdGE9ZHRyYWluLAogICAgICAgICAgICAgICAgbnJvdW5kcz0xNjAsCiAgICAgICAgICAgICAgICB3YXRjaGxpc3Q9bGlzdCh2YWw9ZHRlc3QsdHJhaW49ZHRyYWluKSwKICAgICAgICAgICAgICAgIHByaW50X2V2ZXJ5X249MTAsCiAgICAgICAgICAgICAgICBlYXJseV9zdG9wX3JvdW5kPTEwLAogICAgICAgICAgICAgICAgZXZhbF9tZXRyaWM9ImVycm9yIiwKICAgICAgICAgICAgICAgIGV2YWxfbWV0cmljPSJhdWMiKQoKI21vZGVsIHByZWRpY3Rpb24KeGdicHJlZC5wcm9iPC0gcHJlZGljdCh4Z2IxLGR0ZXN0KQp4Z2JwcmVkLmxhYmVsPC0gaWZlbHNlKHhnYnByZWQucHJvYj4wLjUsMSwwKQpsZXZlbHMoZmFjdG9yKHhnYnByZWQpKQpsZXZlbHMoZmFjdG9yKGxhYmVsX3Rlc3QpKQpsaWJyYXJ5KGNhcmV0KQpjb25mTTwtY29uZnVzaW9uTWF0cml4KGZhY3Rvcih4Z2JwcmVkLmxhYmVsKSxmYWN0b3IobGFiZWxfdGVzdCkpCiNhY2N1cmFjeSAwLjg0MzMKI2JhbGFuY2VkIGFjY3VyYWN5IDAuNjg4NgphdWM8LXJvYyhmYWN0b3IobGFiZWxfdGVzdCkseGdicHJlZC5wcm9iKSRhdWMKI2F1YzowLjg0ODkoaGlnaCkKIyAgdmlldyB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgCm1hdDwtIHhnYi5pbXBvcnRhbmNlKGZlYXR1cmVfbmFtZXM9Y29sbmFtZXMoZmVhdHVyZV90cmFpbiksbW9kZWw9eGdiMSkKeGdiLnBsb3QuaW1wb3J0YW5jZShpbXBvcnRhbmNlX21hdHJpeCA9IG1hdFsxOjIwXSkKc2F2ZShjb25mTSxhdWMsbWF0LGZpbGU9Ii4uL291dHB1dC9yZXNfY3ZfWEdTLnRveS5SRGF0YSIpCn1lbHNlewogIGxvYWQoIi4uL291dHB1dC9yZXNfY3ZfWEdTLnRveS5SRGF0YSIpCn0KCmBgYAoKCmBgYHtyIFhHQnR1bmVfU3RlcDJ9CiN0cnkgdG8gZmluZCB0aGUgbWF4IGRlcHRoIGFuZCBtaW4gY2hpbGQgd2VpZ2h0IAojc291cmNlKCIuLi9saWIvY3Jvc3NfdmFsaWRhdGlvbl9YR1MuUiIpCmh5cGVyX2dyaWRfWEdCXzE8LSBleHBhbmQuZ3JpZChtYXhfZGVwdGg9Mywjc2VxKDMsMTAsYnk9MiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9jaGlsZF93ZWlnaHQ9MSwjc2VxKDEsNixieT0yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlPTAuOCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHNhbXBsZV9ieXRyZWU9MC44CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCnBhcmFsbGVsU3RhcnRTb2NrZXQoY3B1cyA9IGRldGVjdENvcmVzKCkpCmRpbShoeXBlcl9ncmlkX1hHQl8xKQpmZWF0dXJlX3RyYWluID0gYXMubWF0cml4KGRhdF90cmFpblssIC02MDA3XSkKbGFiZWxfdHJhaW4gPSBhcy5pbnRlZ2VyKGRhdF90cmFpbiRsYWJlbCkgCnJ1bi5jdi5YR1MxPVRSVUUKaWYocnVuLmN2LlhHUzEpewogIHJlc19jdl9YR1MxIDwtIG1hdHJpeCgwLCBucm93ID0gbnJvdyhoeXBlcl9ncmlkX1hHQl8xKSwgbmNvbCA9IDQpCiAgZm9yKGkgaW4gMTpucm93KGh5cGVyX2dyaWRfWEdCXzEpKXsKICAgIGw8LWxpc3QobWF4X2RlcHRoPWh5cGVyX2dyaWRfWEdCXzEkbWF4X2RlcHRoW2ldLAogICAgICAgICAgICBtaW5fY2hpbGRfd2VpZ2h0PWh5cGVyX2dyaWRfWEdCXzEkbWluX2NoaWxkX3dlaWdodFtpXSwKICAgICAgICAgICAgc3Vic2FtcGxlPWh5cGVyX2dyaWRfWEdCXzEkc3Vic2FtcGxlW2ldLAogICAgICAgICAgICBjb2xzYW1wbGVfYnl0cmVlPWh5cGVyX2dyaWRfWEdCXzEkY29sc2FtcGxlX2J5dHJlZVtpXSkKICAgIHhnYmN2PC14Z2IuY3YoIHBhcmFtcyA9IGwsCiAgICAgICAgICBkYXRhID0gZHRyYWluLCAKICAgICAgICAgIG5yb3VuZHMgPSAxNjAsIAogICAgICAgICAgbmZvbGQgPSBLLCAKICAgICAgICAgIGdhbW1hPTAsCiAgICAgICAgICBzaG93c2QgPSBULCAKICAgICAgICAgIHN0cmF0aWZpZWQgPSBULCAKICAgICAgICAgIGVhcmx5X3N0b3Bfcm91bmQgPSAxMCwKICAgICAgICAgIHByaW50X2V2ZXJ5X249ODAsCiAgICAgICAgICBldmFsX21ldHJpYz0iYXVjIiwKICAgICAgICAgIGV2YWxfbWV0cmljPSJlcnJvciIsCiAgICAgICAgICBib29zdGVyID0gImdidHJlZSIsCiAgICAgICAgICBvYmplY3RpdmUgPSAiYmluYXJ5OmxvZ2lzdGljIiwgCiAgICAgICAgICBzY2FsZV9wb3Nfd2VpZ2h0PXN1bXduZWcvc3Vtd3BvcywKICAgICAgICAgIGV0YT0wLjMKICAgICAgICAgICkKICAgIHJlc3VsdDwteGdiY3YkZXZhbHVhdGlvbl9sb2cKICAgIGN2LmVycm9yPC1yZXN1bHQkdGVzdF9lcnJvcl9tZWFuCiAgICBjdi5BVUM8LXJlc3VsdCR0ZXN0X2F1Y19tZWFuCiAgICByZXNfY3ZfWEdTMVtpXTwtYyhtZWFuKGN2LmVycm9yKSxzZChjdi5lcnJvciksIG1lYW4oY3YuQVVDKSwgc2QoY3YuQVVDKSkKICBzYXZlKHJlc19jdl9YR1MxLCBmaWxlPSIuLi9vdXRwdXQvcmVzX2N2X1hHUzEuUkRhdGEiKQogIH0KfWVsc2V7CiAgbG9hZCgiLi4vb3V0cHV0L3Jlc19jdl9YR1MxLlJEYXRhIikKfQoKYGBgCgpgYGB7cn0KI25vIG5lZWQgdG8gdHVuZSBnYW1tYQpoeXBlcl9ncmlkX1hHQl8yPC0gZXhwYW5kLmdyaWQobWF4X2RlcHRoPXBhcl9iZXN0X2RlcHRoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX2NoaWxkX3dlaWdodD1wYXJfYmVzdF9jaGlsZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZT1jKDAuNiwwLjcsMC44LDAuOSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzYW1wbGVfYnl0cmVlPWMoMC42LDAuNywwLjgsMC45KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpwYXJhbGxlbFN0YXJ0U29ja2V0KGNwdXMgPSBkZXRlY3RDb3JlcygpKQpkaW0oaHlwZXJfZ3JpZF9YR0JfMikKZmVhdHVyZV90cmFpbiA9IGFzLm1hdHJpeChkYXRfdHJhaW5bLCAtNjAwN10pCmxhYmVsX3RyYWluID0gYXMuaW50ZWdlcihkYXRfdHJhaW4kbGFiZWwpIApydW4uY3YuWEdTMj1UUlVFCmlmKHJ1bi5jdi5YR1MyKXsKICByZXNfY3ZfWEdTMiA8LSBtYXRyaXgoMCwgbnJvdyA9IG5yb3coaHlwZXJfZ3JpZF9YR0JfMSksIG5jb2wgPSA0KQogIGZvcihpIGluIDE6bnJvdyhoeXBlcl9ncmlkX1hHQl8yKSl7CiAgICBsPC1saXN0KG1heF9kZXB0aD1oeXBlcl9ncmlkX1hHQl8yJG1heF9kZXB0aFtpXSwKICAgICAgICAgICAgbWluX2NoaWxkX3dlaWdodD1oeXBlcl9ncmlkX1hHQl8yJG1pbl9jaGlsZF93ZWlnaHRbaV0sCiAgICAgICAgICAgIHN1YnNhbXBsZT1oeXBlcl9ncmlkX1hHQl8yJHN1YnNhbXBsZVtpXSwKICAgICAgICAgICAgY29sc2FtcGxlX2J5dHJlZT1oeXBlcl9ncmlkX1hHQl8yJGNvbHNhbXBsZV9ieXRyZWVbaV0pCiAgICB4Z2JjdjwteGdiLmN2KCBwYXJhbXMgPSBsLAogICAgICAgICAgZGF0YSA9IGR0cmFpbiwgCiAgICAgICAgICBucm91bmRzID0gMTYwLCAKICAgICAgICAgIG5mb2xkID0gSywgCiAgICAgICAgICBnYW1tYT0wLAogICAgICAgICAgc2hvd3NkID0gVCwgCiAgICAgICAgICBzdHJhdGlmaWVkID0gVCwgCiAgICAgICAgICBlYXJseV9zdG9wX3JvdW5kID0gMjAsIAogICAgICAgICAgZXZhbF9tZXRyaWM9ImF1YyIsCiAgICAgICAgICBldmFsX21ldHJpYz0iZXJyb3IiLAogICAgICAgICAgYm9vc3RlciA9ICJnYnRyZWUiLAogICAgICAgICAgb2JqZWN0aXZlID0gImJpbmFyeTpsb2dpc3RpYyIsIAogICAgICAgICAgc2NhbGVfcG9zX3dlaWdodD1zdW13bmVnL3N1bXdwb3MsCiAgICAgICAgICBldGE9MC4zCiAgICAgICAgICApCiAgICByZXN1bHQ8LXhnYmN2JGV2YWx1YXRpb25fbG9nCiAgICBjdi5lcnJvcjwtcmVzdWx0JHRlc3RfZXJyb3JfbWVhbgogICAgY3YuQVVDPC1yZXN1bHQkdGVzdF9hdWNfbWVhbgogICAgcmVzX2N2X1hHUzI8LWMobWVhbihjdi5lcnJvciksc2QoY3YuZXJyb3IpLCBtZWFuKGN2LkFVQyksIHNkKGN2LkFVQykpCiAgc2F2ZShyZXNfY3ZfWEdTMiwgZmlsZT0iLi4vb3V0cHV0L3Jlc19jdl9YR1MyLlJEYXRhIikKICB9Cn1lbHNlewogIGxvYWQoIi4uL291dHB1dC9yZXNfY3ZfWEdTMi5SRGF0YSIpCn0KCmBgYAoKYGBge3J9CiN0dW5lIHRoZSByZWd1bGFyaXphdGlvbiBwYXJhbWV0ZXJzIAoKYGBgCgpgYGB7cn0KIHhnYi5jdiggcGFyYW1zID0gcGFyYW1zLCBkYXRhID0gZHRyYWluLCBucm91bmRzID0gMjAwLCBuZm9sZCA9IDUsIHNob3dzZCA9IFQsIHN0cmF0aWZpZWQgPSBULCBwcmludF9ldmVyeV9uID0gMTAsIGVhcmx5X3N0b3Bfcm91bmQgPSAyMCwgbWF4aW1pemUgPSBGLGV2YWxfbWV0cmljPSJhdWMiLGV2YWxfbWV0cmljPSJlcnJvciIpCgp0cmFpbnRhc2s8LW1ha2VDbGFzc2lmVGFzayhkYXRhPWRhdF90cmFpbix0YXJnZXQ9ImxhYmVsIikKdGVzdHRhc2s8LSBtYWtlQ2xhc3NpZlRhc2soZGF0YT1kYXRfdGVzdCx0YXJnZXQ9ImxhYmVsIikKY3YucGFyYS5zZWFyY2g9VFJVRSAKaWYoY3YucGFyYS5zZWFyY2gpewojY3JlYXRlIGxlYXJuZXIgCmxybl8xIDwtIG1ha2VMZWFybmVyKCJjbGFzc2lmLnhnYm9vc3QiLHByZWRpY3QudHlwZSA9ICJyZXNwb25zZSIpCmxybl8xJHBhci52YWxzIDwtIGxpc3QoIG9iamVjdGl2ZT0iYmluYXJ5OmxvZ2lzdGljIiwgZXZhbF9tZXRyaWM9ImVycm9yIixldmFsX21ldHJpYz0iYXVjIiwgbnJvdW5kcz0yMDBMLCBldGE9MC4zLHNjYWxlX3Bvc193ZWlnaHQ9c3Vtd25lZy9zdW13cG9zKQoKI3NldCBwYXJhbWV0ZXIgc3BhY2UgCnBhcmFtc18xPC0gbWFrZVBhcmFtU2V0KAogIG1ha2VJbnRlZ2VyUGFyYW0oIm1heF9kZXB0aCIsIGxvd2VyPTNMLCB1cHBlcj0xMEwpLAogIG1ha2VOdW1lcmljUGFyYW0oIm1pbl9jaGlsZF93ZWlnaHQiLCBsb3dlcj0xTCwgdXBwZXI9MTBMKSwKICBtYWtlTnVtZXJpY1BhcmFtKCJzdWJzYW1wbGUiLGxvd2VyID0gMC41LHVwcGVyID0gMSksCiAgbWFrZU51bWVyaWNQYXJhbSgiY29sc2FtcGxlX2J5dHJlZSIsbG93ZXIgPSAwLjUsdXBwZXIgPSAxKQopCgojc2V0IHJlc2FtcGxpbmcgc3RyYXRlZ3kgCnJkZXNjXzE8LSBtYWtlUmVzYW1wbGVEZXNjKCJDViIsIHN0cmF0aWZ5PVQsIGl0ZXJzPTVMKQojc2VhcmNoIHN0cmF0ZWd5IApjdHJsXzE8LSBtYWtlVHVuZUNvbnRyb2xSYW5kb20obWF4aXQ9MTAwTCkjMTAwIG1vZGVscwojc2V0IHBhcmFsbGVsIGJhY2tlbmQgCnBhcmFsbGVsU3RhcnRTb2NrZXQoY3B1cyA9IGRldGVjdENvcmVzKCkpCm15dHVuZV8xIDwtIHR1bmVQYXJhbXMobGVhcm5lciA9IGxybl8xLCB0YXNrID0gdHJhaW50YXNrLCByZXNhbXBsaW5nID0gcmRlc2NfMSwgbWVhc3VyZXMgPSBhY2MsIHBhci5zZXQgPSBwYXJhbXNfMSwgY29udHJvbCA9IGN0cmxfMSwgc2hvdy5pbmZvID0gVCkKc2F2ZShteXR1bmVfMSwgZmlsZT0iLi4vb3V0cHV0L3Jlc19jdl9YR1MuU3RlcDIuUkRhdGEiKQp9ZWxzZXsKICAgbG9hZCgiLi4vb3V0cHV0L3Jlc19jdl9YR1MuU3RlcDIuUkRhdGEiKQp9Cm15dHVuZV8xJHkKI3NldCBoeXBlcnBhcmFtZXRlcnMgCmxybl90dW5lXzEgPC0gc2V0SHlwZXJQYXJzKGxybl8xLHBhci52YWxzID0gbXl0dW5lXzEkeCkKI3RyYWluIG1vZGVsIAp4Z21vZGVsXzEgPC0gdHJhaW4obGVhcm5lciA9IGxybl90dW5lXzEsdGFzayA9IHRyYWludGFzaykKI3ByZWRpY3QgbW9kZWwgCnhncHJlZF8xLnByb2I8LSBwcmVkaWN0KHhnbW9kZWxfMSx0ZXN0dGFzaykKY29uZnVzaW9uTWF0cml4KHhncHJlZF8xJGRhdGEkcmVzcG9uc2UseGdwcmVkXzEkZGF0YSR0cnV0aCkjIGFjY3JhY3k9MC44MzUsIGJhbGFuY2VkIGFjY3VyYWN5PTAuNjU0NAphdWNfMTwtcm9jKGZhY3RvcihsYWJlbF90ZXN0KSx4Z2JwcmVkXzEucHJvYikkYXVjICMwLjg0IHNhbWUgYXMgdGhlIGRlZmF1bHQKCiNSZXN1bHQ6T3AuIHBhcnM6IG1heF9kZXB0aD0xMDsgbWluX2NoaWxkX3dlaWdodD05LjkzOyBzdWJzYW1wbGU9MC44NDY7IGNvbHNhbXBsZV9ieXRyZWU9MC42MjcKI2FjYy50ZXN0Lm1lYW49MC44NDQxNzUxLCBzbWFsbGVyIHRoYW4gdGhlIGRlZmF1bHQsIHdoeT8/Pz8/PwojbmVlZCBhdWMgYW5kIGFjY3VyYWN5IGZyb20gY29uZnVzaW9uIG1hdHJpeCAKYGBgCgpgYGB7ciBYR0J0dW5lX3N0ZXAzfQojIHJlZ3VsYXJpenRpb24gdHVuaW5nIAojaGlnaCBkaW1lbnRpb24gdXNpbmcgYWxwaGEgdG8gaGF2ZSBhIHRyeSAKI21heSBiZSAKcGFyYW1fYWxwaGE8LQpgYGAKCgoKIyMjIFN1bW1hcml6ZSBSdW5uaW5nIFRpbWUKUHJlZGljdGlvbiBwZXJmb3JtYW5jZSBtYXR0ZXJzLCBzbyBkb2VzIHRoZSBydW5uaW5nIHRpbWVzIGZvciBjb25zdHJ1Y3RpbmcgZmVhdHVyZXMgYW5kIGZvciB0cmFpbmluZyB0aGUgbW9kZWwsIGVzcGVjaWFsbHkgd2hlbiB0aGUgY29tcHV0YXRpb24gcmVzb3VyY2UgaXMgbGltaXRlZC4gCmBgYHtyIHJ1bm5pbmdfdGltZX0KY2F0KCJUaW1lIGZvciBjb25zdHJ1Y3RpbmcgdHJhaW5pbmcgZmVhdHVyZXM9IiwgdG1fZmVhdHVyZV90cmFpblsxXSwgInMgXG4iKQpjYXQoIlRpbWUgZm9yIGNvbnN0cnVjdGluZyB0ZXN0aW5nIGZlYXR1cmVzPSIsIHRtX2ZlYXR1cmVfdGVzdFsxXSwgInMgXG4iKQpjYXQoIlRpbWUgZm9yIHRyYWluaW5nIG1vZGVsPSIsIHRtX3RyYWluWzFdLCAicyBcbiIpIApjYXQoIlRpbWUgZm9yIHRlc3RpbmcgbW9kZWw9IiwgdG1fdGVzdFsxXSwgInMgXG4iKQpgYGAKCiMjI1JlZmVyZW5jZQotIER1LCBTLiwgVGFvLCBZLiwgJiBNYXJ0aW5leiwgQS4gTS4gKDIwMTQpLiBDb21wb3VuZCBmYWNpYWwgZXhwcmVzc2lvbnMgb2YgZW1vdGlvbi4gUHJvY2VlZGluZ3Mgb2YgdGhlIE5hdGlvbmFsIEFjYWRlbXkgb2YgU2NpZW5jZXMsIDExMSgxNSksIEUxNDU0LUUxNDYyLgo=